package org.redis.cluster.config;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.redis.cluster.Configs;
import org.redis.cluster.config.exception.ConfigException;
import org.redis.cluster.util.Assert;
import org.redis.cluster.util.XmlUtil;

public class XmlConfigLoader implements ConfigLoader {
	
	private final static String DEFAULT_CONFIG_FILE="server.xml";
	
	private final static String ROOT_ELEMENT="redis-cluster";
	
	//server
	private final static String SERVER_ELEMENT="server";
	private final static String SERVER_TYPE_ATTR="type";
	private final static String SERVER_HOST_ELEMENT="host";
	private final static String SERVER_PORT_ELEMENT="port";
	private final static String SERVER_THREAD_ELEMENT="thread";
	private final static String SERVER_BUFFER_SIZE_ELEMENT="buffer-size";
	private final static String SERVER_TIMEOUT_ELEMENT="timeout";
	private final static String SERVER_CONNECTION_ELEMENT="connection";
	
	//registry
	private final static String REGISTRY_ELEMENT="registry";
	private final static String REGISTRY_TYPE_ATTR="type";
	private final static String REGISTRY_HOST_ELEMENT="host";
	private final static String REGISTRY_PORT_ELEMENT="port";
	private final static String REGISTRY_TIMEOUT_ELEMENT="timeout";
	private final static String REGISTRY_ROOT_ELEMENT="root";
	
	//system
	private final static String SYSTEM_ELEMENT="system";
	private final static String SYSTEM_PROPERTY_ELEMENT="property";
	private final static String SYSTEM_PROPERTY_NAME_ATTR="name";
	
	private Document document;
	
	private String config=DEFAULT_CONFIG_FILE;

	@Override
	public ServerConfig loadServerConfig() {
		Document document=read();
		
		Element root=(Element) document.selectSingleNode(ROOT_ELEMENT);
		Assert.notNull(root,"%s 根结点不存在",ROOT_ELEMENT);
		
		Element serverElement=(Element) root.selectSingleNode(SERVER_ELEMENT);
		Assert.notNull(root,"%s 结点不存在",SERVER_ELEMENT);
		
		Element hostElement=(Element) root.selectSingleNode(SERVER_HOST_ELEMENT);
		if(hostElement!=null) {
			Assert.hasLength(hostElement.getTextTrim(), "%s 不能为空", SERVER_HOST_ELEMENT);
		}
		
		Element portElement=(Element) serverElement.selectSingleNode(SERVER_PORT_ELEMENT);
		if(portElement!=null) {
			Assert.mustNumber(portElement.getTextTrim(), "%s 必须为数字", SERVER_PORT_ELEMENT);
		}
		
		Element threadElement=(Element) serverElement.selectSingleNode(SERVER_THREAD_ELEMENT);
		if(threadElement!=null) {
			Assert.mustNumber(threadElement.getTextTrim(), "%s 必须为数字", SERVER_THREAD_ELEMENT);
		}
		
		Element bufferSizeElement=(Element) serverElement.selectSingleNode(SERVER_BUFFER_SIZE_ELEMENT);
		if(bufferSizeElement!=null) {
			Assert.mustNumber(bufferSizeElement.getTextTrim(), "%s 必须为数字", SERVER_BUFFER_SIZE_ELEMENT);
		}
		
		Element timeoutElement=(Element) serverElement.selectSingleNode(SERVER_TIMEOUT_ELEMENT);
		if(timeoutElement!=null) {
			Assert.mustNumber(timeoutElement.getTextTrim(), "%s 必须为数字", SERVER_TIMEOUT_ELEMENT);
		}
		
		Element connectionElement=(Element) serverElement.selectSingleNode(SERVER_CONNECTION_ELEMENT);
		if(connectionElement!=null) {
			Assert.mustNumber(connectionElement.getTextTrim(), "%s 必须为数字", SERVER_CONNECTION_ELEMENT);
		}
		
		String type=serverElement.attributeValue(SERVER_TYPE_ATTR);
		String host=(hostElement==null?Configs.DEFAULT_SERVER_HOST:hostElement.getTextTrim());
		int port=(portElement==null?Configs.DEFAULT_SERVER_PORT:Integer.parseInt(portElement.getTextTrim()));
		int thread=(threadElement==null?Configs.DEFAULT_SERVER_THREAD:Integer.parseInt(threadElement.getTextTrim()));
		int bufferSize=(bufferSizeElement==null?Configs.DEFAULT_SERVER_BUFFER_SIZE:Integer.parseInt(bufferSizeElement.getTextTrim()));
		int timeout=(timeoutElement==null?Configs.DEFAULT_SERVER_TIMEOUT:Integer.parseInt(timeoutElement.getTextTrim()));
		int connection=(connectionElement==null?Configs.DEFAULT_SERVER_CONNECTION:Integer.parseInt(connectionElement.getTextTrim()));
		
		ServerConfig config=new ServerConfig();
		config.setServer(type);
		config.setHost(host);
		config.setPort(port);
		config.setThread(thread);
		config.setBufferSize(bufferSize);
		config.setTimeout(timeout);
		config.setConnection(connection);
		return config;
	}

	@Override
	public RegistryConfig loadRegistryConfig() {
		Document document=read();
		
		Element root=(Element) document.selectSingleNode(ROOT_ELEMENT);
		Assert.notNull(root,"%s 根结点不存在",ROOT_ELEMENT);
		
		Element registryElement=(Element) root.selectSingleNode(REGISTRY_ELEMENT);
		Assert.notNull(root,"%s 结点不存在",REGISTRY_ELEMENT);
		
		Element hostElement=(Element) registryElement.selectSingleNode(REGISTRY_HOST_ELEMENT);
		Assert.notNull(hostElement, "%s 不能为空", REGISTRY_HOST_ELEMENT);
		Assert.hasLength(hostElement.getTextTrim(), "%s 不能为空", REGISTRY_HOST_ELEMENT);
		
		Element portElement=(Element) registryElement.selectSingleNode(REGISTRY_PORT_ELEMENT);
		if(portElement!=null) {
			Assert.mustNumber(portElement.getTextTrim(), "%s 必须为数字", REGISTRY_PORT_ELEMENT);
		}
		
		Element timeoutElement=(Element) registryElement.selectSingleNode(REGISTRY_TIMEOUT_ELEMENT);
		if(timeoutElement!=null) {
			Assert.mustNumber(timeoutElement.getTextTrim(), "%s 必须为数字", REGISTRY_TIMEOUT_ELEMENT);
		}
		
		Element rootElement=(Element) registryElement.selectSingleNode(REGISTRY_ROOT_ELEMENT);
		Assert.notNull(rootElement, "%s 不能为空", REGISTRY_ROOT_ELEMENT);
		Assert.hasLength(rootElement.getTextTrim(), "%s 不能为空", REGISTRY_ROOT_ELEMENT);
		
		String type=registryElement.attributeValue(REGISTRY_TYPE_ATTR);
		String host=hostElement.getTextTrim();
		int port=Integer.parseInt(portElement.getTextTrim());
		int timeout=(timeoutElement==null?Configs.DEFAULT_REGISTRY_TIMEOUT:Integer.parseInt(timeoutElement.getTextTrim()));
		String rootStr=(rootElement==null?Configs.DEFAULT_REGISTRY_ROOT:rootElement.getTextTrim());
		
		RegistryConfig config=new RegistryConfig();
		config.setRegistry(type);
		config.setHost(host);
		config.setPort(port);
		config.setTimeout(timeout);
		config.setRoot(rootStr);
		return config;
	}
	
	@Override
	public SystemConfig loadSystemConfig() {
		Document document=read();
		
		Element root=(Element) document.selectSingleNode(ROOT_ELEMENT);
		Assert.notNull(root,"%s 根结点不存在",ROOT_ELEMENT);
		
		Element systemElement=(Element) root.selectSingleNode(SYSTEM_ELEMENT);
		if(systemElement==null) {
			return new SystemConfig(Collections.emptyList());
		}
		
		List<Element> propertyElements=systemElement.selectNodes(SYSTEM_PROPERTY_ELEMENT);
		Collection<SystemPropertyConfig> propertyConfigs=(propertyElements==null?Collections.emptyList():new ArrayList<SystemPropertyConfig>(propertyElements.size()));
		
		if(CollectionUtils.isNotEmpty(propertyElements)) {
			for(Element propertyElement:propertyElements) {
				String name=propertyElement.attributeValue(SYSTEM_PROPERTY_NAME_ATTR);
				Assert.hasLength(name,"%s 不能为空",SYSTEM_PROPERTY_NAME_ATTR);
				
				String value=(propertyElement.hasContent()?propertyElement.getTextTrim():null);
				
				SystemPropertyConfig propertyConfig=new SystemPropertyConfig(name, value);
				propertyConfigs.add(propertyConfig);
			}
		}
		
		return new SystemConfig(propertyConfigs);
	}
	
	protected Document read() {
		if(this.document!=null) {
			return this.document;
		}
		
		URL url=getClass().getClassLoader().getResource(getConfig());
		Assert.notNull(url,"%s 不存在",getConfig());
		
		try {
			Document document=read(url);
			this.document=document;
			return document;
		}
		catch(Exception e) {
			throw new ConfigException(e);
		}
	}
	
	protected Document read(URL url) throws Exception {
		return XmlUtil.read(url.openStream());
	}
	
	public void setConfig(String config) {
		this.config = config;
	}
	public String getConfig() {
		return config;
	}
}